<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html>
<head>
<title>GtkTreeView Widget</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="GUI, GtkTreeView Widget, GTK+ library, C">
<meta name="description" content="GtkTreeView Widget">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>


<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>GtkTreeView Widget</h1>

<p>
In this part of the GTK+ programming tutorial, we will work with a 
GtkTreeView widget. 
</p>

<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div>

<p>
<b class="keyword">GtkTreeView</b> widget is a complex widget which can be 
used to display lists and trees. 
The widget can have one or multiple columns. The GtkTreeView widget has a 
MVC (Model View Controller) design architecture.
This means, that the data is separated from the view. 
</p>

<p>
There are several other objects that are used with the GtkTreeView widget. 
The <b class="keyword">GtkCellRenderer</b> determines, how the data is going 
to be displayed in the 
<b class="keyword">GtkTreeViewColumn</b>. 
The <b class="keyword">GtkListStore</b> and the <b class="keyword">GtkTreeStore</b> 
represent the model. They handle data, that are displayed
in the GtkTreeView widget. <b class="keyword">GtkTreeIter</b> is a structure 
used to refer to a row in the GtkTreeView.
<b class="keyword">GtkTreeSelection</b> is a an object that handles selections.
</p>


<h2>Simple List View</h2>

<p>
The first example will show a simple list view. We will display textual data. 
</p>

<pre class="code">
#include &lt;gtk/gtk.h&gt;

enum
{
  LIST_ITEM = 0,
  N_COLUMNS
};

static void
init_list(GtkWidget *list)
{

  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;
  GtkListStore *store;

  renderer = gtk_cell_renderer_text_new();
  column = gtk_tree_view_column_new_with_attributes("List Items",
          renderer, "text", LIST_ITEM, NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);

  store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING);

  gtk_tree_view_set_model(GTK_TREE_VIEW(list), 
      GTK_TREE_MODEL(store));

  g_object_unref(store);
}

static void
add_to_list(GtkWidget *list, const gchar *str)
{
  GtkListStore *store;
  GtkTreeIter iter;

  store = GTK_LIST_STORE(gtk_tree_view_get_model
      (GTK_TREE_VIEW(list)));

  gtk_list_store_append(store, &amp;iter);
  gtk_list_store_set(store, &amp;iter, LIST_ITEM, str, -1);
}


void  on_changed(GtkWidget *widget, gpointer label) 
{
  GtkTreeIter iter;
  GtkTreeModel *model;
  char *value;


  if (gtk_tree_selection_get_selected(
      GTK_TREE_SELECTION(widget), &amp;model, &amp;iter)) {

    gtk_tree_model_get(model, &amp;iter, LIST_ITEM, &amp;value,  -1);
    gtk_label_set_text(GTK_LABEL(label), value);
    g_free(value);
  }

}

int main (int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *list;

  GtkWidget *vbox;
  GtkWidget *label;
  GtkTreeSelection *selection; 

  gtk_init(&amp;argc, &amp;argv);


  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_container_set_border_width(GTK_CONTAINER(window), 10);
  gtk_widget_set_size_request(window, 270, 250);
  gtk_window_set_title(GTK_WINDOW(window), "List View");

  list = gtk_tree_view_new();
  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);

  vbox = gtk_vbox_new(FALSE, 0);

  gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 5);

  label = gtk_label_new("");
  gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
  gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);

  gtk_container_add(GTK_CONTAINER(window), vbox);

  init_list(list);
  add_to_list(list, "Aliens");
  add_to_list(list, "Leon");
  add_to_list(list, "Capote");
  add_to_list(list, "Saving private Ryan");
  add_to_list(list, "Der Untergang");


  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));

  g_signal_connect(selection, "changed", 
      G_CALLBACK(on_changed), label);

  g_signal_connect(G_OBJECT (window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main ();

  return 0;
}
</pre>

<p>
In our code example, we will show 5 items in the GtkTreeView. 
We will have only one column and the header of the column will be hidden. 
We will place a <b class="keyword">GtkVBox</b> into the window. This box will have two widgets. A 
<b class="keyword">GtkTreeView</b> and a <b class="keyword">GtkLabel</b>.
</p>

<pre class="explanation">
list = gtk_tree_view_new();
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
</pre>

<p>
The <b class="keyword">GtkTreeView</b> is created and the columns are hidden.
</p>

<pre class="explanation">
label = gtk_label_new("");
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
</pre>

<p>
The <b class="keyword">GtkLabel</b> is created, centered and placed below 
the <b class="keyword">GtkTreeView</b>
</p>

<pre class="explanation">
 init_list(list);
</pre>

<p>
This function initializes the list. 
</p>

<pre class="explanation">
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes("List Items",
        renderer, "text", LIST_ITEM, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
</pre>

<p>
Inside that function, we create and append one single column. 
</p>

<pre class="explanation">
store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING);

gtk_tree_view_set_model(GTK_TREE_VIEW(list), 
    GTK_TREE_MODEL(store));
</pre>

<p>
We create a <b class="keyword">GtkListStore</b> (a model) and set it to the list. 
</p>

<pre class="explanation">
 g_object_unref(store);
</pre>

<p>
The model is destroyed automatically with the view. 
</p>

<pre class="explanation">
add_to_list(list, "Aliens");
</pre>

<p>
This user function adds an option to the list. 
</p>

<pre class="explanation">
 store = GTK_LIST_STORE(gtk_tree_view_get_model
     (GTK_TREE_VIEW(list)));

 gtk_list_store_append(store, &amp;iter);
 gtk_list_store_set(store, &amp;iter, LIST_ITEM, str, -1);
</pre>

<p>
Inside the <b class="keyword">add_to_list()</b> function, we get the model using the 
<b class="keyword">gtk_tree_view_get_model()</b> function call. We append a new 
row and set a value to the row, which is reference by an <b class="keyword">GtkTreeIter</b> object.
</p>

<pre class="explanation">
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
</pre>

<p>
The <b class="keyword">GtkTreeSelection</b> need not to be created explicitly. It is automatically created
with the <b class="keyword">GtkTreeView</b> widget. The reference to the widget is obtained using
the <b class="keyword">gtk_tree_view_get_selection()</b> function call. 
</p>

<pre class="explanation">
g_signal_connect(selection, "changed", 
    G_CALLBACK(on_changed), label);
</pre>

<p>
Upon change signal of the <b class="keyword">GtkTreeSelection</b>, 
we call the <b class="keyword">on_changed()</b>
handler. 
</p>

<pre class="explanation">
gtk_tree_model_get(model, &amp;iter, LIST_ITEM, &amp;value,  -1);
gtk_label_set_text(GTK_LABEL(label), value);
</pre>

<p>
Inside the handler function, we get the value of the cell in the row referenced by the iter object. 
</p>

<br>
<img src="/img/gui/cgtk/listview.png" alt="List View">
<div class="figure">Figure: List View</div>
<br>


<h2>Advanced List View</h2>

<p>
The second example will add additional functionality to the previous one. 
We will be able to add and remove items from the list view.  
</p>

<pre class="code">
#include &lt;gtk/gtk.h&gt;

enum
{
  LIST_ITEM = 0,
  N_COLUMNS
};

GtkWidget *list;


static void
append_item(GtkWidget *widget, gpointer entry)
{
  GtkListStore *store;
  GtkTreeIter  iter;

  const char *str = gtk_entry_get_text(entry); 

  store = GTK_LIST_STORE(gtk_tree_view_get_model(
       GTK_TREE_VIEW(list)));

  gtk_list_store_append(store, &amp;iter);
  gtk_list_store_set(store, &amp;iter, LIST_ITEM, str, -1);
}

static void
remove_item(GtkWidget *widget, gpointer selection)
{
  GtkListStore *store;
  GtkTreeModel *model;
  GtkTreeIter  iter;


  store = GTK_LIST_STORE(gtk_tree_view_get_model(
      GTK_TREE_VIEW (list)));
  model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));

  if (gtk_tree_model_get_iter_first(model, &amp;iter) == FALSE) 
      return;

  if (gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection), 
      &amp;model, &amp;iter)) {
    gtk_list_store_remove(store, &amp;iter);
  }
}

static void
remove_all(GtkWidget *widget, gpointer selection)
{
  GtkListStore *store;
  GtkTreeModel *model;
  GtkTreeIter  iter;


  store = GTK_LIST_STORE(gtk_tree_view_get_model(
      GTK_TREE_VIEW (list)));
  model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));

  if (gtk_tree_model_get_iter_first(model, &amp;iter) == FALSE) 
      return;
  gtk_list_store_clear(store);
}

static void
init_list(GtkWidget *list)
{

  GtkCellRenderer    *renderer;
  GtkTreeViewColumn  *column;
  GtkListStore       *store;

  renderer = gtk_cell_renderer_text_new();
  column = gtk_tree_view_column_new_with_attributes("List Item",
          renderer, "text", LIST_ITEM, NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW (list), column);

  store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING);

  gtk_tree_view_set_model(GTK_TREE_VIEW (list), 
      GTK_TREE_MODEL(store));

  g_object_unref(store);
}


int main (int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *sw;

  GtkWidget *remove;
  GtkWidget *add;
  GtkWidget *removeAll;
  GtkWidget *entry;

  GtkWidget *vbox;
  GtkWidget *hbox;

  GtkTreeSelection *selection; 

  gtk_init(&amp;argc, &amp;argv);


  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  sw = gtk_scrolled_window_new(NULL, NULL);
  list = gtk_tree_view_new();

  gtk_window_set_title (GTK_WINDOW (window), "List View");
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  gtk_widget_set_size_request (window, 370, 270);

  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw),
            GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw),
            GTK_SHADOW_ETCHED_IN);

  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);

  vbox = gtk_vbox_new(FALSE, 0);

  gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 5);

  hbox = gtk_hbox_new(TRUE, 5);

  add = gtk_button_new_with_label("Add");
  remove = gtk_button_new_with_label("Remove");
  removeAll = gtk_button_new_with_label("Remove All");
  entry = gtk_entry_new();

  gtk_box_pack_start(GTK_BOX(hbox), add, FALSE, TRUE, 3);
  gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, TRUE, 3);
  gtk_box_pack_start(GTK_BOX(hbox), remove, FALSE, TRUE, 3);
  gtk_box_pack_start(GTK_BOX(hbox), removeAll, FALSE, TRUE, 3);

  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 3);

  gtk_container_add(GTK_CONTAINER (sw), list);
  gtk_container_add(GTK_CONTAINER (window), vbox);

  init_list(list);

  selection  = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));

  g_signal_connect(G_OBJECT(add), "clicked",
          G_CALLBACK(append_item), entry);

  g_signal_connect(G_OBJECT(remove), "clicked",
          G_CALLBACK(remove_item), selection);

  g_signal_connect(G_OBJECT(removeAll), "clicked",
          G_CALLBACK(remove_all), selection);

  g_signal_connect (G_OBJECT (window), "destroy",
          G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main ();

  return 0;
}
</pre>

<p>
Instead of a label, we create three buttons and one text entry. 
We will be able to add a new option dynamically and remove the currently 
selected option or remove all list items. 
</p>

<pre class="explanation">
 sw = gtk_scrolled_window_new(NULL, NULL);
 ...

 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw),
           GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw),
           GTK_SHADOW_ETCHED_IN);

 ...
 gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 5);
 ...
 gtk_container_add(GTK_CONTAINER (sw), list);
</pre>

<p>
The <b class="keyword">GtkTreeView</b> is placed inside the scrolled window. 
</p> 

<pre class="explanation">
if (gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection), 
    &amp;model, &amp;iter)) {
  gtk_list_store_remove(store, &amp;iter);
}
</pre>

<p>
The <b class="keyword">gtk_list_store_remove()</b> function removes an item from the list. 
</p>

<pre class="explanation">
gtk_list_store_clear(store);
</pre>

<p>
The <b class="keyword">gtk_list_store_clear()</b> will remove all items from the list. 
</p>

<pre class="explanation">
if (gtk_tree_model_get_iter_first(model, &amp;iter) == FALSE) 
    return;
</pre>

<p>
This code checks if there is some item left in the list. Obviously, we 
can remove items only if there is at least one
left in the list. 
</p>


<br>
<img src="/img/gui/cgtk/manage.png" alt="Advanced List View">
<div class="figure">Figure: Advanced List View</div>
<br>


<h2>Tree View</h2>

<p>
Next we will use the <b class="keyword">GtkTreeView</b> widget to display 
hierarchical data. In the previous two examples, we
used the list view, now we are going to use tree view. 
</p>

<pre class="code">
#include &lt;gtk/gtk.h&gt;

enum
{
  COLUMN = 0,
  NUM_COLS
} ;

void  on_changed(GtkWidget *widget, gpointer statusbar) 
{
  GtkTreeIter iter;
  GtkTreeModel *model;
  char *value;


  if (gtk_tree_selection_get_selected(
      GTK_TREE_SELECTION(widget), &amp;model, &amp;iter)) {

    gtk_tree_model_get(model, &amp;iter, COLUMN, &amp;value,  -1);
    gtk_statusbar_push(GTK_STATUSBAR(statusbar),
        gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar), 
            value), value);
    g_free(value);
  }
}


static GtkTreeModel *
create_and_fill_model (void)
{
  GtkTreeStore *treestore;
  GtkTreeIter toplevel, child;

  treestore = gtk_tree_store_new(NUM_COLS,
                  G_TYPE_STRING);

  gtk_tree_store_append(treestore, &amp;toplevel, NULL);
  gtk_tree_store_set(treestore, &amp;toplevel,
                     COLUMN, "Scripting languages",
                     -1);

  gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
  gtk_tree_store_set(treestore, &amp;child,
                     COLUMN, "Python",
                     -1);
  gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
  gtk_tree_store_set(treestore, &amp;child,
                     COLUMN, "Perl",
                     -1);
  gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
  gtk_tree_store_set(treestore, &amp;child,
                     COLUMN, "PHP",
                     -1);

  gtk_tree_store_append(treestore, &amp;toplevel, NULL);
  gtk_tree_store_set(treestore, &amp;toplevel,
                     COLUMN, "Compiled languages",
                     -1);

  gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
  gtk_tree_store_set(treestore, &amp;child,
                     COLUMN, "C",
                     -1);

  gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
  gtk_tree_store_set(treestore, &amp;child,
                     COLUMN, "C++",
                     -1);

  gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
  gtk_tree_store_set(treestore, &amp;child,
                     COLUMN, "Java",
                     -1);

  return GTK_TREE_MODEL(treestore);
}



static GtkWidget *
create_view_and_model (void)
{
  GtkTreeViewColumn *col;
  GtkCellRenderer *renderer;
  GtkWidget *view;
  GtkTreeModel *model;

  view = gtk_tree_view_new();

  col = gtk_tree_view_column_new();
  gtk_tree_view_column_set_title(col, "Programming languages");
  gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);

  renderer = gtk_cell_renderer_text_new();
  gtk_tree_view_column_pack_start(col, renderer, TRUE);
  gtk_tree_view_column_add_attribute(col, renderer, 
      "text", COLUMN);

  model = create_and_fill_model();
  gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
  g_object_unref(model); 

  return view;
}


int
main (int argc, char **argv)
{
  GtkWidget *window;
  GtkWidget *view;
  GtkTreeSelection *selection; 
  GtkWidget *vbox;
  GtkWidget *statusbar;

  gtk_init(&amp;argc, &amp;argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_title(GTK_WINDOW(window), "Tree View");
  gtk_widget_set_size_request (window, 350, 300);


  vbox = gtk_vbox_new(FALSE, 2);
  gtk_container_add(GTK_CONTAINER(window), vbox);


  view = create_view_and_model();
  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));

  gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 1);

  statusbar = gtk_statusbar_new();
  gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, TRUE, 1);

  g_signal_connect(selection, "changed", 
      G_CALLBACK(on_changed), statusbar);

  g_signal_connect (G_OBJECT (window), "destroy",
          G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}
</pre>

<p>
In our example, we divide programming languages into two groups. 
Scripting languages and compiled languages. The language categories 
serve as toplevel nodes for their list of items. The currently selected node or item
is shown in the statusbar. 
</p>

<p>
The steps to create a tree view are very similar to creating a list view. 
</p>

<pre class="explanation">
GtkTreeStore *treestore;
</pre>

<p>
Here we use a different model. A <b class="keyword">GtkTreeStore</b>. 
</p>


<pre class="explanation">
treestore = gtk_tree_store_new(NUM_COLS,
                G_TYPE_STRING);
</pre>

<p>
We create a <b class="keyword">GtkTreeStore</b> with only one column. 
</p>

<pre class="explanation">
gtk_tree_store_append(treestore, &amp;toplevel, NULL);
gtk_tree_store_set(treestore, &amp;toplevel,
                  COLUMN, "Scripting languages",
                  -1);
</pre>

<p>
This creates a toplevel node. 
</p>

<pre class="explanation">
gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
gtk_tree_store_set(treestore, &amp;child,
                  COLUMN, "Python",
                  -1);
</pre>

<p>
This code creates a child item.
</p>

<br>
<img src="/img/gui/cgtk/treeview.png" alt="Tree View">
<div class="figure">Figure: Tree View</div>
<br>


<hr class="btm">

<p>
In this chapter we covered GtkTreeView widget. 
</p>

<br>
<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div>
<br>


<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified April 2, 2008  <span class="copyright">&copy; 2007 - 2012 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>
